namespace SimpleWebServices
{
    using System;
    using System.Runtime.Serialization;
    using System.Security.Permissions;

    [Serializable]
    public sealed class RelativeUri : IComparable, IComparable<RelativeUri>, IEquatable<RelativeUri>, ISerializable
    {
        private Uri _value;

        public RelativeUri(string value)
            : this(null == value ? null as Uri : new Uri(value, UriKind.RelativeOrAbsolute))
        {
        }

        public RelativeUri(Uri value)
        {
            this.Value = value;
        }

        private RelativeUri(SerializationInfo info, StreamingContext context)
        {
            this._value = new Uri(info.GetString("_value"), UriKind.Relative);
        }

        public int Length
        {
            get
            {
                return this.ToString().Length;
            }
        }

        private Uri Value
        {
            get
            {
                return this._value;
            }

            set
            {
                if (null == value)
                {
                    throw new ArgumentNullException("value");
                }
                else if (value.IsAbsoluteUri)
                {
                    throw new UriFormatException();
                }

                this._value = value;
            }
        }

        public static bool operator ==(RelativeUri obj, RelativeUri comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = obj.Equals(comparand);
            }

            return result;
        }

        public static bool operator !=(RelativeUri obj, RelativeUri comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = !object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = !obj.Equals(comparand);
            }

            return result;
        }

        public static bool operator <(RelativeUri obj, RelativeUri comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = !object.ReferenceEquals(null, comparand);
            }
            else
            {
                result = 0 > obj.CompareTo(comparand);
            }

            return result;
        }

        public static bool operator >(RelativeUri obj, RelativeUri comparand)
        {
            bool result = false;

            if (object.ReferenceEquals(null, obj))
            {
                result = false;
            }
            else
            {
                result = 0 < obj.CompareTo(comparand);
            }

            return result;
        }

        public static implicit operator string(RelativeUri uri)
        {
            return (null == uri) ? null as string : uri.Value.OriginalString;
        }

        public static implicit operator RelativeUri(string value)
        {
            return (null == value) ? null as RelativeUri : (RelativeUri)new Uri(value, UriKind.Relative);
        }

        public static implicit operator Uri(RelativeUri uri)
        {
            return (null == uri) ? null as Uri : uri.Value;
        }

        public static implicit operator RelativeUri(Uri value)
        {
            return (null == value) ? null as RelativeUri : new RelativeUri(value);
        }

        public int CompareTo(RelativeUri other)
        {
            int result = 0;

            if (null == other)
            {
                result = 1;
            }
            else
            {
                result = string.CompareOrdinal(this.Value.OriginalString, other.Value.OriginalString);
            }

            return result;
        }

        public int CompareTo(object obj)
        {
            int result = 0;

            if (null == obj)
            {
                result = 1;
            }
            else
            {
                RelativeUri cast = obj as RelativeUri;
                if (null == cast)
                {
                    throw new ArgumentOutOfRangeException("obj");
                }

                result = string.CompareOrdinal(this.Value.OriginalString, cast.Value.OriginalString);
            }

            return result;
        }

        public bool Equals(RelativeUri other)
        {
            bool result = false;

            if (!object.ReferenceEquals(null, other))
            {
                if (object.ReferenceEquals(this, other))
                {
                    result = true;
                }
                else
                {
                    result = 0 == this.CompareTo(other);
                }
            }

            return result;
        }

        public override bool Equals(object obj)
        {
            bool result = false;

            if (!object.ReferenceEquals(null, obj))
            {
                if (object.ReferenceEquals(this, obj))
                {
                    result = true;
                }
                else
                {
                    RelativeUri cast = obj as RelativeUri;

                    if (!object.ReferenceEquals(null, cast))
                    {
                        result = this.Value == cast.Value;
                    }
                }
            }

            return result;
        }

        public override int GetHashCode()
        {
            return this.ToString().GetHashCode();
        }

        public override string ToString()
        {
            return this.Value.OriginalString;
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (null == info)
            {
                throw new ArgumentNullException("info");
            }

            info.AddValue("_value", this._value.OriginalString);
        }
    }
}